import crypto from 'crypto';
import child_process from 'child_process';
export function isEmpty(str) {
	return str === null || str === '' || str === undefined;
}

export function endWith(str, suffix) {
	let reg = new RegExp(suffix + '$');
	return reg.test(str);
}

export function startWith(str, prefix) {
	let reg = new RegExp('^' + prefix);
	return reg.test(str);
}

export function getRandomArbitrary(min, max) {
	return Math.random() * (max - min) + min;
}

export function sleep(ms) {
	return new Promise(resolve => setTimeout(resolve, ms));
}
export function arrayRemove(arr, value) {
	if (typeof value != 'function') {
		let ov = value;
		value = function(ele) {
			return ele != ov;
		};
	}
	return arr.filter(value);
}

export let bindThis = function(obj, ...args) {
	for (let k in args) {
		obj[args[k]] = obj[args[k]].bind(obj);
	}
};

export const makeCancelable = promise => {
	let hasCanceled_ = false;

	const wrappedPromise = new Promise((resolve, reject) => {
		promise.then(val => (hasCanceled_ ? reject({ isCanceled: true }) : resolve(val)), error => (hasCanceled_ ? reject({ isCanceled: true }) : reject(error)));
	});

	return {
		promise: wrappedPromise,
		cancel() {
			hasCanceled_ = true;
		},
	};
};

export const promiseTimeout = function(ms, promise, msg) {
	// console.log('ms----', ms, 'msg----', msg);
	// Create a promise that rejects in <ms> milliseconds
	let timeout = new Promise((resolve, reject) => {
		let id = setTimeout(() => {
			// console.log('checktimeout', Date.now());
			clearTimeout(id);
			reject(msg || `promise2 timeout: ${ms}ms.`);
		}, ms);
		// console.log('createtimeout', Date.now(), ms);
	});
	// console.log('promise---', promise, 'timeout----', timeout);
	// Returns a race between our timeout and the passed in promise
	return Promise.race([promise, timeout]);
};

export function convertArrayBufferToBuffer(ab) {
	let buffer = new Buffer(ab.byteLength);
	let view = new Uint8Array(ab);
	for (let i = 0; i < buffer.length; ++i) {
		buffer[i] = view[i];
	}
	return buffer;
}

export function createSocketIOHelper(socket) {
	let seqAuto = 0;
	let sendAction = function(type, payload) {
		if (!socket.id) throw 'socket not connected';
		let seq = ++seqAuto;
		let action = { type, payload, seq };
		socket.emit('action', action);
		return seq;
	};
	return {
		waitAction: async function(filter, timeout = 3000) {
			let listener;
			let pro = new Promise((fill, reject) => {
				listener = action => {
					// console.log('socket wait recv', action);
					if (!filter || filter(action)) {
						if (action.error) reject(action.error);
						else fill(action);
					}
				};
				socket.on('action', listener);
			});
			if (timeout != 0) pro = promiseTimeout(timeout, pro, `等待服务器数据包超时`);
			try {
				let ret = await pro;
				return ret;
			} catch (e) {
				throw e;
			} finally {
				socket.removeListener('action', listener);
			}
		},
		sendActionAndWait: async function(type, payload = {}, timeout = 3000) {
			let seq = sendAction(type, payload);
			let listener;
			let pro = promiseTimeout(
				timeout,
				new Promise((fill, reject) => {
					listener = action => {
						// console.log('socket wait recv', action);
						if (action.seq == seq) {
							if (action.error) reject(action.error);
							else fill(action);
						}
					};
					socket.on('action', listener);
				}),
				`等待服务器响应${type}超时`,
			);
			try {
				let ret = await pro;
				return ret;
			} catch (e) {
				throw e;
			} finally {
				socket.removeListener('action', listener);
			}
		},
	};
}

export function createIntervalClicker(fn, dt, name) {
	let locked = false;

	console.log('create clicker', dt, name);
	return function(d) {
		if (locked) {
			console.log('locked clicker', name);
			return;
		}
		fn(d);
		locked = true;
		setTimeout(() => {
			locked = false;
		}, dt);
	};
}
export function getDateTimeNow() {
	return new Date(new Date().toLocaleDateString()).getTime();
}

export interface SexTimeoutParam {
	dt: number;
	atonce?: boolean;
	count?: number;
	handler: (step: number, clear: () => void) => void;
}
export function sexTimeout(param: SexTimeoutParam) {
	let tm = null;
	let step = 0;
	let clear = () => {
		clearTimeout(tm);
	};
	tm = setTimeout(() => {
		param.handler(step, clear);
		if (++step == param.count) {
			clear();
		}
	}, param.dt);
	if (param.atonce) {
		param.handler(step++, clear);
	}
}

// DES 加密
export function desEncrypt(message, key) {
	key = key.length >= 8 ? key.slice(0, 8) : key.concat('0'.repeat(8 - key.length));
	const keyHex = Buffer.from(key);
	const cipher = crypto.createCipheriv('des-cbc', keyHex, keyHex);
	let c = cipher.update(message, 'utf8', 'base64');
	c += cipher.final('base64');
	return c;
}

// DES 解密
export function desDecrypt(text, key) {
	key = key.length >= 8 ? key.slice(0, 8) : key.concat('0'.repeat(8 - key.length));
	const keyHex = Buffer.from(key);
	const cipher = crypto.createDecipheriv('des-cbc', keyHex, keyHex);
	let c = cipher.update(text, 'base64', 'utf8');
	c += cipher.final('utf8');
	return c;
}

export function convertUint8ArrayToBinaryString(u8Array: Uint8Array) {
	let i,
		len = u8Array.length,
		b_str = '';
	for (i = 0; i < len; i++) {
		b_str += String.fromCharCode(u8Array[i]);
	}
	return b_str;
}

export function convertBinaryStringToUint8Array(bStr: string): Uint8Array {
	let i,
		len = bStr.length,
		u8_array = new Uint8Array(len);
	for (let i = 0; i < len; i++) {
		u8_array[i] = bStr.charCodeAt(i);
	}
	return u8_array;
}

export let shell = function(cwd: string, cmd: string, opt?: { [key: string]: any }) {
	try {
		console.log(cmd);
		let opt_env = (opt && opt.env) || {};
		let r = child_process.execSync(cmd, {
			stdio: 'inherit',
			cwd,
			...opt,
		});
		return r ? r.toString() : undefined;
	} catch (e) {
		if (opt && opt.noerror) {
			console.log('ignore error', e.message);
		} else throw e;
	}
};
